﻿namespace UnityEditor.Graphs
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using UnityEditor;
    using UnityEditorInternal;
    using UnityEngine;

    public class Graph : ScriptableObject
    {
        [CompilerGenerated]
        private static Func<UnityEditor.Graphs.Node, int> <>f__am$cache6;
        [CompilerGenerated]
        private static Func<UnityEditor.Graphs.Node, IEnumerable<Slot>> <>f__am$cache7;
        [SerializeField]
        public List<Edge> edges = new List<Edge>();
        [NonSerialized]
        internal Rect graphExtents;
        [NonSerialized]
        private List<Slot> m_changingOutputSlotTypesCycleSlots = new List<Slot>();
        [NonSerialized]
        private bool m_ImAwake;
        [SerializeField]
        internal List<Edge> m_InvalidEdges = new List<Edge>();
        [SerializeField]
        public List<UnityEditor.Graphs.Node> nodes = new List<UnityEditor.Graphs.Node>();

        public virtual void AddNode(UnityEditor.Graphs.Node node)
        {
            this.AddNode(node, true);
            this.Dirty();
        }

        internal virtual void AddNode(UnityEditor.Graphs.Node node, bool serialize)
        {
            this.nodes.Add(node);
            node.graph = this;
            this.Dirty();
            node.AddedToGraph();
        }

        public virtual void AddNodes(params UnityEditor.Graphs.Node[] nodes)
        {
            foreach (UnityEditor.Graphs.Node node in nodes)
            {
                this.AddNode(node);
            }
        }

        public virtual bool CanConnect(Slot fromSlot, Slot toSlot)
        {
            return true;
        }

        public virtual void Clear()
        {
            foreach (UnityEditor.Graphs.Node node in this.nodes)
            {
                node.RemovingFromGraph();
                this.RemoveEdgesFromNode(node);
            }
            this.nodes.Clear();
            this.Dirty();
        }

        public virtual Edge Connect(Slot fromSlot, Slot toSlot)
        {
            <Connect>c__AnonStorey7 storey = new <Connect>c__AnonStorey7 {
                fromSlot = fromSlot,
                toSlot = toSlot
            };
            bool flag = this.m_changingOutputSlotTypesCycleSlots.Count == 0;
            if (this.m_changingOutputSlotTypesCycleSlots.Contains(storey.toSlot))
            {
                storey.toSlot.edges.RemoveAll(new Predicate<Edge>(storey.<>m__4));
                storey.fromSlot.edges.RemoveAll(new Predicate<Edge>(storey.<>m__5));
                this.edges.RemoveAll(new Predicate<Edge>(storey.<>m__6));
                throw new ArgumentException("Connecting node data slots this way creates infinite cycle of changing node types");
            }
            this.m_changingOutputSlotTypesCycleSlots.Add(storey.toSlot);
            if ((storey.fromSlot == null) || (storey.toSlot == null))
            {
                throw new ArgumentException("to/from slot can't be null");
            }
            if (this.Connected(storey.fromSlot, storey.toSlot))
            {
                throw new ArgumentException("Already connected");
            }
            Edge item = new Edge(storey.fromSlot, storey.toSlot);
            this.edges.Add(item);
            SetGenericPropertyArgumentType(storey.toSlot, storey.fromSlot.dataType);
            this.Dirty();
            storey.toSlot.node.InputEdgeChanged(item);
            if (flag)
            {
                this.m_changingOutputSlotTypesCycleSlots.Clear();
            }
            return item;
        }

        public virtual bool Connected(Slot fromSlot, Slot toSlot)
        {
            <Connected>c__AnonStorey6 storey = new <Connected>c__AnonStorey6 {
                fromSlot = fromSlot,
                toSlot = toSlot
            };
            return this.edges.Exists(new Predicate<Edge>(storey.<>m__3));
        }

        public virtual void DestroyNode(UnityEditor.Graphs.Node node)
        {
            this.RemoveNode(node);
            UnityEngine.Object.DestroyImmediate(node, true);
        }

        public virtual void Dirty()
        {
            EditorUtility.SetDirty(this);
        }

        private void DoWakeUpEdges(List<Edge> inEdges, List<Edge> ok, List<Edge> error, bool inEdgesUsedToBeValid)
        {
            foreach (Edge edge in inEdges)
            {
                if (edge != null)
                {
                    if (edge.NodesNotNull())
                    {
                        if (edge.WakeUp())
                        {
                            ok.Add(edge);
                            if (!inEdgesUsedToBeValid)
                            {
                                this.Dirty();
                            }
                        }
                        else
                        {
                            error.Add(edge);
                            if (inEdgesUsedToBeValid)
                            {
                                this.Dirty();
                            }
                        }
                    }
                }
                else
                {
                    Debug.LogError("Edge is null?");
                }
            }
        }

        public static Graph FlattenedCopy(Graph source)
        {
            Dictionary<UnityEditor.Graphs.Node, UnityEditor.Graphs.Node> dictionary = new Dictionary<UnityEditor.Graphs.Node, UnityEditor.Graphs.Node>();
            Graph graph = (Graph) Activator.CreateInstance(source.GetType());
            foreach (UnityEditor.Graphs.Node node in source.nodes)
            {
                UnityEditor.Graphs.Node dest = (UnityEditor.Graphs.Node) Activator.CreateInstance(node.GetType());
                EditorUtility.CopySerialized(node, dest);
                dictionary.Add(node, dest);
                graph.AddNode(dest);
            }
            graph.OnEnable();
            foreach (Edge edge in source.edges)
            {
                UnityEditor.Graphs.Node node3 = edge.fromSlot.node;
                UnityEditor.Graphs.Node node4 = edge.toSlot.node;
                node3 = dictionary[node3];
                node4 = dictionary[node4];
                Slot fromSlot = node3[edge.fromSlot.name];
                Slot toSlot = node4[edge.toSlot.name];
                graph.Connect(fromSlot, toSlot);
            }
            return graph;
        }

        public static string GenerateName()
        {
            return InternalGraphUtility.GenerateGraphName();
        }

        internal virtual GraphGUI GetEditor()
        {
            GraphGUI hgui = ScriptableObject.CreateInstance<GraphGUI>();
            hgui.graph = this;
            return hgui;
        }

        public UnityEditor.Graphs.Node GetNodeByName(string name)
        {
            foreach (UnityEditor.Graphs.Node node in this.nodes)
            {
                if (node.name == name)
                {
                    return node;
                }
            }
            return null;
        }

        internal static int[] GetNodeIdsForSerialization(Graph graph)
        {
            if (<>f__am$cache6 == null)
            {
                <>f__am$cache6 = n => n.GetInstanceID();
            }
            return Enumerable.Select<UnityEditor.Graphs.Node, int>(graph.nodes, <>f__am$cache6).ToArray<int>();
        }

        public void OnEnable()
        {
            this.WakeUp();
        }

        public void RedirectSlotEdges(UnityEditor.Graphs.Node node, string oldSlotName, string newSlotName)
        {
            foreach (Edge edge in this.edges)
            {
                if (edge.fromSlotName == oldSlotName)
                {
                    edge.fromSlotName = newSlotName;
                }
                if (edge.toSlotName == oldSlotName)
                {
                    edge.toSlotName = newSlotName;
                }
            }
        }

        public virtual void RemoveEdge(Edge e)
        {
            this.edges.Remove(e);
            if (e.fromSlot != null)
            {
                e.fromSlot.RemoveEdge(e);
            }
            if (e.toSlot != null)
            {
                e.toSlot.RemoveEdge(e);
                e.toSlot.node.InputEdgeChanged(e);
            }
            ResetGenericPropertyArgumentType(e.toSlot);
            this.Dirty();
        }

        private void RemoveEdgesFromNode(UnityEditor.Graphs.Node node)
        {
            List<Edge> list = new List<Edge>();
            IEnumerator<Edge> enumerator = node.inputEdges.GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    Edge current = enumerator.Current;
                    list.Add(current);
                }
            }
            finally
            {
                if (enumerator == null)
                {
                }
                enumerator.Dispose();
            }
            IEnumerator<Edge> enumerator2 = node.outputEdges.GetEnumerator();
            try
            {
                while (enumerator2.MoveNext())
                {
                    Edge item = enumerator2.Current;
                    list.Add(item);
                }
            }
            finally
            {
                if (enumerator2 == null)
                {
                }
                enumerator2.Dispose();
            }
            foreach (Edge edge3 in list)
            {
                this.RemoveEdge(edge3);
            }
        }

        public void RemoveInvalidEdgesForSlot(Slot slot)
        {
            <RemoveInvalidEdgesForSlot>c__AnonStoreyA ya = new <RemoveInvalidEdgesForSlot>c__AnonStoreyA {
                slot = slot
            };
            this.m_InvalidEdges.RemoveAll(new Predicate<Edge>(ya.<>m__D));
        }

        public virtual void RemoveNode(UnityEditor.Graphs.Node node)
        {
            if (node == null)
            {
                throw new ArgumentNullException("Node is null");
            }
            node.RemovingFromGraph();
            this.RemoveEdgesFromNode(node);
            this.nodes.Remove(node);
            this.Dirty();
        }

        public virtual void RemoveNodes(List<UnityEditor.Graphs.Node> nodesToRemove)
        {
            foreach (UnityEditor.Graphs.Node node in nodesToRemove)
            {
                this.RemoveNode(node);
            }
        }

        private static void ResetGenericPropertyArgumentType(Slot toSlot)
        {
            if ((toSlot != null) && ((toSlot.isInputDataSlot && (toSlot.node.genericType != null)) && (toSlot.node.isGeneric && (toSlot.node.inputDataSlots.First<Slot>() == toSlot))))
            {
                toSlot.node.ResetGenericPropertyArgumentType();
            }
        }

        public void RevalidateInputDataEdges(Slot s)
        {
            <RevalidateInputDataEdges>c__AnonStorey8 storey = new <RevalidateInputDataEdges>c__AnonStorey8 {
                s = s
            };
            if (!storey.s.isDataSlot || (storey.s.type != SlotType.InputSlot))
            {
                throw new ArgumentException("Expected an input data slot");
            }
            if (storey.s.edges.Count<Edge>() > 1)
            {
                throw new ArgumentException("Got input data slot with multiple input Edges. This should never happen.");
            }
            if (storey.s.edges.Count<Edge>() == 1)
            {
                Edge item = storey.s.edges.First<Edge>();
                item.fromSlot.edges.Remove(item);
                storey.s.edges.Clear();
                this.edges.Remove(item);
                if (this.CanConnect(item.fromSlot, item.toSlot))
                {
                    if (this.m_changingOutputSlotTypesCycleSlots.Contains(storey.s))
                    {
                        this.m_changingOutputSlotTypesCycleSlots.Remove(storey.s);
                    }
                    this.Connect(item.fromSlot, storey.s);
                }
                else
                {
                    this.m_InvalidEdges.Add(item);
                    item.toSlot.node.InputEdgeChanged(item);
                }
            }
            else
            {
                IEnumerator<Edge> enumerator = Enumerable.Where<Edge>(this.m_InvalidEdges, new Func<Edge, bool>(storey.<>m__8)).GetEnumerator();
                try
                {
                    while (enumerator.MoveNext())
                    {
                        Edge current = enumerator.Current;
                        if (this.CanConnect(current.fromSlot, current.toSlot))
                        {
                            this.Connect(current.fromSlot, current.toSlot);
                            this.m_InvalidEdges.Remove(current);
                            return;
                        }
                    }
                }
                finally
                {
                    if (enumerator == null)
                    {
                    }
                    enumerator.Dispose();
                }
            }
        }

        public void RevalidateOutputDataEdges(Slot s)
        {
            <RevalidateOutputDataEdges>c__AnonStorey9 storey = new <RevalidateOutputDataEdges>c__AnonStorey9 {
                s = s
            };
            if (!storey.s.isDataSlot || (storey.s.type != SlotType.OutputSlot))
            {
                throw new ArgumentException("Expected an output data slot");
            }
            List<Edge> list = storey.s.edges.ToList<Edge>();
            List<Edge> list2 = Enumerable.Where<Edge>(this.m_InvalidEdges, new Func<Edge, bool>(storey.<>m__9)).ToList<Edge>();
            foreach (Edge edge in storey.s.edges)
            {
                edge.toSlot.edges.RemoveAll(new Predicate<Edge>(storey.<>m__A));
            }
            storey.s.edges.Clear();
            this.edges.RemoveAll(new Predicate<Edge>(storey.<>m__B));
            this.m_InvalidEdges.RemoveAll(new Predicate<Edge>(storey.<>m__C));
            foreach (Edge edge2 in list)
            {
                if (this.CanConnect(edge2.fromSlot, edge2.toSlot))
                {
                    this.Connect(edge2.fromSlot, edge2.toSlot);
                }
                else
                {
                    this.m_InvalidEdges.Add(edge2);
                    edge2.toSlot.node.InputEdgeChanged(edge2);
                }
            }
            foreach (Edge edge3 in list2)
            {
                if (this.CanConnect(edge3.fromSlot, edge3.toSlot))
                {
                    this.Connect(edge3.fromSlot, edge3.toSlot);
                }
                else
                {
                    this.m_InvalidEdges.Add(edge3);
                }
            }
        }

        private static void SetGenericPropertyArgumentType(Slot toSlot, System.Type fromSlotType)
        {
            if ((toSlot.isInputDataSlot && (toSlot.node.genericType == null)) && (toSlot.node.isGeneric && (toSlot.node.inputDataSlots.First<Slot>() == toSlot)))
            {
                toSlot.node.SetGenericPropertyArgumentType(SerializedType.GenericType(fromSlotType));
            }
        }

        public override string ToString()
        {
            string str = "[";
            foreach (UnityEditor.Graphs.Node node in this.nodes)
            {
                str = str + node + "|";
            }
            str = str + "];[";
            foreach (Edge edge in this.edges)
            {
                if (edge != null)
                {
                    str = str + edge + "|";
                }
            }
            return (str + "]");
        }

        public void WakeUp()
        {
            this.WakeUp(false);
        }

        public virtual void WakeUp(bool force)
        {
            if (force || !this.m_ImAwake)
            {
                for (int i = this.nodes.Count - 1; i >= 0; i--)
                {
                    if (this.nodes[i] == null)
                    {
                        Debug.LogError("Removing null node");
                        this.nodes.RemoveAt(i);
                    }
                }
                foreach (UnityEditor.Graphs.Node node in this.nodes)
                {
                    node.Awake();
                }
                this.WakeUpNodes();
                if (this.edges != null)
                {
                    this.WakeUpEdges(false);
                }
                else
                {
                    Debug.LogError("Edges are null?");
                }
                this.m_ImAwake = true;
            }
        }

        public void WakeUpEdges(bool clearSlotEdges)
        {
            if (clearSlotEdges)
            {
                if (<>f__am$cache7 == null)
                {
                    <>f__am$cache7 = n => n.slots;
                }
                IEnumerator<Slot> enumerator = Enumerable.SelectMany<UnityEditor.Graphs.Node, Slot>(this.nodes, <>f__am$cache7).GetEnumerator();
                try
                {
                    while (enumerator.MoveNext())
                    {
                        Slot current = enumerator.Current;
                        current.edges.Clear();
                    }
                }
                finally
                {
                    if (enumerator == null)
                    {
                    }
                    enumerator.Dispose();
                }
            }
            List<Edge> ok = new List<Edge>();
            List<Edge> error = new List<Edge>();
            this.DoWakeUpEdges(this.edges, ok, error, true);
            this.DoWakeUpEdges(this.m_InvalidEdges, ok, error, false);
            this.edges = ok;
            this.m_InvalidEdges = error;
        }

        protected virtual void WakeUpNodes()
        {
            foreach (UnityEditor.Graphs.Node node in this.nodes)
            {
                node.WakeUp(this);
            }
        }

        public UnityEditor.Graphs.Node this[string name]
        {
            get
            {
                return this.GetNodeByName(name);
            }
        }

        [CompilerGenerated]
        private sealed class <Connect>c__AnonStorey7
        {
            internal Slot fromSlot;
            internal Slot toSlot;

            internal bool <>m__4(Edge edg)
            {
                return (edg.fromSlot == this.fromSlot);
            }

            internal bool <>m__5(Edge edg)
            {
                return (edg.toSlot == this.toSlot);
            }

            internal bool <>m__6(Edge edg)
            {
                return ((edg.fromSlot == this.fromSlot) && (edg.toSlot == this.toSlot));
            }
        }

        [CompilerGenerated]
        private sealed class <Connected>c__AnonStorey6
        {
            internal Slot fromSlot;
            internal Slot toSlot;

            internal bool <>m__3(Edge e)
            {
                return ((e.fromSlot == this.fromSlot) && (e.toSlot == this.toSlot));
            }
        }

        [CompilerGenerated]
        private sealed class <RemoveInvalidEdgesForSlot>c__AnonStoreyA
        {
            internal Slot slot;

            internal bool <>m__D(Edge e)
            {
                return ((e.fromSlot == this.slot) || (e.toSlot == this.slot));
            }
        }

        [CompilerGenerated]
        private sealed class <RevalidateInputDataEdges>c__AnonStorey8
        {
            internal Slot s;

            internal bool <>m__8(Edge e)
            {
                return (e.toSlot == this.s);
            }
        }

        [CompilerGenerated]
        private sealed class <RevalidateOutputDataEdges>c__AnonStorey9
        {
            internal Slot s;

            internal bool <>m__9(Edge e)
            {
                return (e.fromSlot == this.s);
            }

            internal bool <>m__A(Edge edg)
            {
                return (edg.fromSlot == this.s);
            }

            internal bool <>m__B(Edge e)
            {
                return (e.fromSlot == this.s);
            }

            internal bool <>m__C(Edge e)
            {
                return (e.fromSlot == this.s);
            }
        }
    }
}

